<?php
/**
 * 简单的 断言 功能的实现,仅能在 Kenxu_Unit_TestCase的子类中调用
 *
 * @author KenXu
 * @date: 2010-7-28 下午12:18:27
 */
class Kenxu_Unit_Assert {
	
	/**
	 * 使用断言的次数
	 * @var $_count integer
	 */
	private static $_count = 0 ;
	
	/**
	 * 断言失败时 错误信息的存放位置,格式如下:
	 * 	array(
	 * 		':class' => array(
	 * 			':method' => :msg
	 * 		)
	 * 	)
	 * @var array
	 */
	protected static $_assertionFailedTrace = array() ;
	
	/**
	 * 按测试用例的类/方法名称 获取 断言失败时 的错误信息集合
	 * 
	 * @param string $class
	 * @param string $method
	 * @return array | null
	 */
	static function getAssertionFailedTrace($class,$method=null){
		if (!empty($class) && is_string($class) && isset(self::$_assertionFailedTrace[$class])){
			$classTrace = self::$_assertionFailedTrace[$class] ;
			if (empty($method)) return $classTrace ;
			if (is_string($method) && isset($classTrace[$method]))
				return $classTrace[$method] ;
		}
		return null ;
	}
	
	/**
	 * 用一组规则 测试值,每个规则的第一个元素是 回调函数,成功返回true,否则为false
	 * 
	 * @param mixed $value 值
	 * @param array $rules 测试规则
	 * @param string $description 测试目的字符串
	 * @return boolean
	 */
	static function assertThat($value,array $rules=null,$description=null){
		self::$_count ++ ;
		
		$validator = Kenxu_Unit_Validate::getInstance() ;
		/* @var $validator Kenxu_Unit_Validate */
		if (!$validator->not_null_array($rules)) return true ;
		
		$errors = array(); // $fld -> errorInfo 
		foreach ($rules as $index=>$rule)
			// $rule => array(rule, validationParams, errorInfo)
			$errors[Kenxu_Core::callbackToString($rule[0])] = array_pop($rules[$index]); // 弹出错误信息
		
		$failed = null ;
		
		if ($validator->validateBatch($value,$rules,false,$failed)) return true ;
		
		try {
			$failedRule = Kenxu_Core::callbackToString(array_pop($failed)) ;
			throw new Kenxu_Unit_AssertionFailed($failedRule,$errors[$failedRule],$description);
		} catch (Kenxu_Unit_AssertionFailed $ex) {
			
			self::_fail($ex) ;
		}
		return false ;
	}
	
	static function assertNotNull($value,$description=null){
//		return self::assertThat($value,array(array('not_empty','值不能为空')),$description);
		Kenxu_Unit_Assert::assertThat( $value,array(array('not_empty','值不能为空')) ,'测试图书元素' );
	}
	
	/**
	 * 断言失败时抛出的异常信息捕获
	 * 
	 * @param Kenxu_Unit_AssertionFailed $ex
	 */
	protected static function _fail(Kenxu_Unit_AssertionFailed $ex){
		$traces = $ex->getTrace();
		
//		Kenxu_Core::dump($traces,'错误$traces') ;
		
		// 1是测试用例的测试方法代码的信息
		$testMethodTrace = $traces[1] ;
		
		$testcaseClass = $testMethodTrace['class'] ;
		$testMethod = $testMethodTrace['function'] ;
		
		// 验证初始化信息
		if (!isset(self::$_assertionFailedTrace[$testcaseClass]))
			self::$_assertionFailedTrace[$testcaseClass] = array() ;
		if (!isset(self::$_assertionFailedTrace[$testcaseClass][$testMethod]))
			self::$_assertionFailedTrace[$testcaseClass][$testMethod] = array() ;
		
		// 0 是断言调用处代码的信息
		$assertFailedTrace = $traces[0] ;	
		
		// 调用代码字符串
		$argsType = array_map('gettype',$assertFailedTrace['args']);
		$code = sprintf("{$assertFailedTrace['class']}{$assertFailedTrace['type']}{$assertFailedTrace['function']}(%s)",
			implode(',',$argsType));		
		
		// 操作  $assertTrace 信息
		self::$_assertionFailedTrace[$testcaseClass][$testMethod][] = array(
			'assertDestination' => $ex->getMessage() , // 断言目的
			'code' => $code , // 调用代码
			'line' => $assertFailedTrace['line'] , // 代码行			
			'failedRule' => $ex->getAssertRule() , // 失败规则
			'failedMessage' => $ex->getAssertMessage() // 失败规则的验证信息
		) ;
//		
//		Kenxu_Core::dump($testMethodTrace);
//		Kenxu_Core::dump($assertFailedTrace);
		
		unset($ex) ;
	}
	
	/**
	 * 返回当前执行的断言次数
	 * @return int
	 */
	static function getCount(){ return self::$_count ;}
}